﻿using System.IO;

namespace NMemcached.Client.Binary
{
    /// <summary>
    /// Encapsulates the functionality for a binary request.
    /// </summary>
    internal class BinaryRequest : BinaryPacket
    {
        /// <summary>
        /// Identifies the packet as a request
        /// </summary>
        internal override PacketType Type { get { return PacketType.Request; } }

        /// <summary>
        /// Defines a no operation packet
        /// </summary>
        internal static readonly BinaryRequest NoOperation;

        static BinaryRequest()
        {
            //Initializes the no operation packet
            BinaryRequest.NoOperation = new BinaryRequest(OpCode.NoOperation);
        }

        /// <summary>
        /// Initializes a new instance of the BinaryHeader class
        /// </summary>
        internal BinaryRequest()
            : this(OpCode.NoOperation)
        {
        }

        /// <summary>
        /// Initializes a new instance of the BinaryHeader class
        /// </summary>
        /// <param name="operation">Sets the packet operation code</param>
        internal BinaryRequest(OpCode operation)
        {
            this.Operation = operation;
            this.DataType = DataTypeCode.Raw;
            this.Opaque = 0;
            this.Cas = 0;
        }

        /// <summary>
        /// Writes the content of the binary packet to a byte array that conforms to the memcached binary protocol
        /// </summary>
        /// <param name="endianMode">The byte order to use for primitives when creating the byte array</param>
        /// <returns>The content of the binary packet that conforms to the memcached binary protocol</returns>
        internal byte[] ToBytes(Endian endianMode)
        {
            using (MemoryStream outputStream = new MemoryStream())
            {
                using (EndianBinaryWriter writer = new EndianBinaryWriter(outputStream, endianMode))
                {
                    //Write the magic byte
                    writer.Write((byte)this.Type);
                    //Set the operation
                    writer.Write((byte)this.Operation);
                    //Specify the key and extras lengths
                    writer.Write(this.KeyLength);
                    writer.Write(this.ExtrasLength);
                    //The data type code is always raw, for now
                    writer.Write((byte)this.DataType);
                    //Reserved 16 bits
                    writer.Write((ushort)0);
                    //The size of the key, extras, and content
                    writer.Write(this.TotalBodyLength);
                    //A value echoed back in the response to verify the response is for the request
                    writer.Write(this.Opaque);
                    //The check and set value
                    writer.Write(this.Cas);

                    //Write the extras, key, and values
                    if (this.Extras != null)
                    {
                        writer.Write(this.Extras);
                    }

                    if (this.Key != null)
                    {
                        writer.Write(this.Key);
                    }

                    if (this.Value != null)
                    {
                        writer.Write(this.Value);
                    }

                    byte[] outputBytes = outputStream.ToArray();
                    return outputBytes;
                }
            }
        }
    }
}
